﻿using System;
using System.Xml;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using SdmxMl.Common;
using SdmxMl.Helper;

namespace SdmxMl.Structure
{
    /// <summary> SDMX 2.0 Textype types </summary>
    public enum TextTypeType
    {
        Undefined,
        String,
        Boolean,
        Decimal,
        Float,
        Double,
        Integer,
        BigInteger,
        Long,
        Short,
        Duration,
        DateTime,
        Time,
        Date,
        Year,
        Month,
        Day,
        MonthDay,
        YearMonth,
        URI,
        Timespan,
        Count,
        InclusiveValueRange,
        ExclusiveValueRange,
        Incremental,
        ObservationalTimePeriod,
    }

    public struct TextTypeBoxes
    {
        public TextTypeType txType;
        public bool isSeq;
        public bool isMinLen;
        public bool isMaxLen;
        public bool isStart;
        public bool isEnd;
        public bool isPattern;
        public bool isInterval;
        public bool isTimeInt;
        public bool isDecimal;
        public bool isMultiLingual;

        public TextTypeBoxes(TextTypeType txType, bool isSeq, bool isMinLen, bool isMaxLen, bool isStart, bool isEnd,
            bool isPattern, bool isInterval, bool isTimeInt, bool isDecimal, bool isMultiLingual)
        {
            this.txType = txType;
            this.isSeq = isSeq;
            this.isMinLen = isMinLen;
            this.isMaxLen = isMaxLen;
            this.isStart = isStart;
            this.isEnd = isEnd;
            this.isPattern = isPattern;
            this.isInterval = isInterval;
            this.isTimeInt = isTimeInt;
            this.isDecimal = isDecimal;
            this.isMultiLingual = isMultiLingual;
        }
    }

    /// <summary>
    /// TextFormatType defines the information for describing a text format. 
    /// If the TextType attribute is not specified, any valid characters may be included 
    /// in the text field. (It corresponds to the xs:string datatype of W3C XML Schema.) 
    /// The textType attribute provides a description of the data type, and may place 
    /// restrictions on the values of the other attributes, referred to as "facets". 
    /// The isSequence attribute indicates whether the values are intended to be ordered, 
    /// and it may work in combination with the interval attribute. The minLength and 
    /// maxLength attributes specify the minimum and maximum lengths of the value in 
    /// characters. startValue and endValue are used for inclusive and exclusive ranges, 
    /// indicating what the bounds of the range are. The interval attribute specifies the 
    /// permitted interval between two values. The timeInterval attribute indicates the 
    /// permitted duration between two time expressions. The decimals attribute indicates 
    /// the number of characters allowed after the decimal separator. The pattern attribute 
    /// holds any regular expression permitted in the simila facet in W3C XML Schema.
    /// </summary>
    public class TextFormatType
    {
        private static TextTypeBoxes[] TextTypeBoxEnable = new TextTypeBoxes[] {
                              //                        seq     min    max  start  end    pattern inter  timei  decimal, multiLingual                     
            new TextTypeBoxes(TextTypeType.Undefined,  false, false, false, false, false, false, false, false, false, false), 
            new TextTypeBoxes(TextTypeType.BigInteger, true , true,  true,  true , true , true , true, false, false, false), 
            new TextTypeBoxes(TextTypeType.String,     false, true , true , false, false, true , false, false, false, true), 
            new TextTypeBoxes(TextTypeType.Boolean,    false, false, false, false, false, true , false, false, false, false), 
            new TextTypeBoxes(TextTypeType.Decimal,    false, true,  true,  true , true , true , false, false, true, false ), 
            new TextTypeBoxes(TextTypeType.Count,      true , false, false, true , true , false, false , true, false, false), 
            new TextTypeBoxes(TextTypeType.Date,       false, false, false, true , true , false, false , true, false, false), 
            new TextTypeBoxes(TextTypeType.DateTime,   false, false, false, true , true , true , false , true, false, false), 
            new TextTypeBoxes(TextTypeType.Day,        false, false, false, true , true , true , false, false, false, false), 
            new TextTypeBoxes(TextTypeType.Double,     false, false, false, true , true , true , false, false, true, false ), 
            new TextTypeBoxes(TextTypeType.Duration,   false, false, false, true , true , true , false, true , false, false), 
   new TextTypeBoxes(TextTypeType.ExclusiveValueRange, false, false, false, true , true , false, false, false, true, false ), 
            new TextTypeBoxes(TextTypeType.Float,      false, false, false, true , true , true , false, false, true, false ), 
   new TextTypeBoxes(TextTypeType.InclusiveValueRange, false, false, false, true , true , false, false, false, true, false ), 
            new TextTypeBoxes(TextTypeType.Incremental,true , false, false, true , true , false, true , false, false, false), 
            new TextTypeBoxes(TextTypeType.Integer,    true , true,  true,  true , true , true , true, false, false, false), 
            new TextTypeBoxes(TextTypeType.Long,       false, false, false, true , true , true , true, false, false, false), 
            new TextTypeBoxes(TextTypeType.Month,      false, false, false, true , true , true , false, false, false, false), 
            new TextTypeBoxes(TextTypeType.MonthDay,   false, false, false, true , true , true , false, false, false, false), 
new TextTypeBoxes(TextTypeType.ObservationalTimePeriod,false, false, false, true , true , true , false, true , false, false), 
            new TextTypeBoxes(TextTypeType.Short,      false, false, false, true , true , true , true, false, false, false), 
            new TextTypeBoxes(TextTypeType.Time,       false, false, false, true , true , true , false, false, false, false), 
            new TextTypeBoxes(TextTypeType.Timespan,   false, false, false, true , true , true , false, false, false, false), 
            new TextTypeBoxes(TextTypeType.URI,        false, true , true , false, false, true , false, false, false, false), 
            new TextTypeBoxes(TextTypeType.Year,       false, false, false, true , true , true , false, false, false, false), 
            new TextTypeBoxes(TextTypeType.YearMonth,  false, false, false, true , true , true , false, false, false, false), 

        };

		TextTypeType textType;

        public TextTypeType TextType
        {
            get { return textType; }
            set { textType = value; }
        }
		
        public bool isSequence;
        public string minLength;
        public string maxLength;
        public string startValue;
        public string endValue;
        public string interval;
        public string timeInterval;
		public string decimals;
        public string pattern;
        public bool isMultiLingual;

        private void Init()
        {
            textType = TextTypeType.Undefined;
        }

        public TextFormatType()
        {
            Init();
        }

        // Clone C'tor
        public TextFormatType(TextFormatType clone)
        {
            textType = clone.TextType;
            isSequence = clone.isSequence;
            minLength = clone.minLength;
            maxLength = clone.maxLength;
            startValue = clone.startValue;
            endValue = clone.endValue;
            interval = clone.interval;
            timeInterval = clone.timeInterval;
            decimals = clone.decimals;
            pattern = clone.pattern;
            isMultiLingual = clone.isMultiLingual;
        }

        public TextFormatType(XmlNode nodeArtefact, XmlNamespaceManager nsmgr) 
        {
            Init();
            XmlNode nodeFormat = nodeArtefact.SelectSingleNode("./structure:TextFormat", nsmgr);
            FillFormat(nodeFormat);
        }
        public TextFormatType(XmlNode nodeArtefact, string selTag, XmlNamespaceManager nsmgr)
        {
            Init();
            XmlNode nodeFormat = nodeArtefact.SelectSingleNode(selTag, nsmgr);
            FillFormat(nodeFormat);
        }

        private void FillFormat(XmlNode nodeFormat)
        {
            if (nodeFormat != null)
            {
                string tt = Xmlhelper.GetAtt(nodeFormat, "textType");
                textType = GetTypeFromString(tt);
                isMultiLingual = textType == TextTypeType.String;
                // SDMX 2,1 can held multilingual info
                if (Xmlhelper.GetAtt(nodeFormat, "isMultiLingual").ToLower() == "false")
                    isMultiLingual = false;

                isSequence = Xmlhelper.GetAtt(nodeFormat, "isSequence").ToLower() == "true";
                minLength = Xmlhelper.GetAtt(nodeFormat, "minLength");
                maxLength = Xmlhelper.GetAtt(nodeFormat, "maxLength");
                startValue = Xmlhelper.GetAtt(nodeFormat, "startValue");
                endValue = Xmlhelper.GetAtt(nodeFormat, "endValue");
                interval = Xmlhelper.GetAtt(nodeFormat, "interval");
                timeInterval = Xmlhelper.GetAtt(nodeFormat, "timeInterval");
                decimals = Xmlhelper.GetAtt(nodeFormat, "decimals");
                pattern = Xmlhelper.GetAtt(nodeFormat, "pattern");
            }
        }

        public void WriteXml(XmlTextWriter xw, string prefix, string tag)
        {
            if (textType != TextTypeType.Undefined)
            {
                xw.WriteStartElement(prefix, tag, null);

                string str = textType.ToString();
                if (SdmxNamespaceManager.SdmxTarget == SdmxVersion.SDMX_21 && str == "Year")
                    str = "GregorianYear";

                Xmlhelper.WriteAttributeIfExist(xw, "textType",str);

                // correct if startvalue we must have interval and isSequence set
                if (string.IsNullOrEmpty(startValue) == false)
                    isSequence = true;

                if (isSequence)
                {
                    xw.WriteAttributeString("isSequence", isSequence.ToString().ToLower());
                    if (string.IsNullOrEmpty(interval) && IsNumeric)
                        interval = "1";
                }

                Xmlhelper.WriteAttributeIfExist(xw, "minLength", minLength);
                Xmlhelper.WriteAttributeIfExist(xw, "maxLength", maxLength);
                Xmlhelper.WriteAttributeIfExist(xw, "startValue", startValue);
                Xmlhelper.WriteAttributeIfExist(xw, "endValue", endValue);
                Xmlhelper.WriteAttributeIfExist(xw, "interval", interval);
                Xmlhelper.WriteAttributeIfExist(xw, "timeInterval", timeInterval);
                Xmlhelper.WriteAttributeIfExist(xw, "decimals", decimals);
                Xmlhelper.WriteAttributeIfExist(xw, "pattern", pattern);


                xw.WriteEndElement();
            }
        }


        public void WriteXmlAsSimpleCodeDataType(XmlTextWriter xw, string prefix, string tag)
        {
            if (textType != TextTypeType.Undefined)
            {
                xw.WriteStartElement(prefix, tag, null);
                string simpleCodeDataType = "AlphaNumeric";
                if (IsNumeric)
                    simpleCodeDataType = "Numeric";

                Xmlhelper.WriteAttributeIfExist(xw, "textType", simpleCodeDataType);
                if (isSequence)
                    xw.WriteAttributeString("isSequence", isSequence.ToString().ToLower());
                Xmlhelper.WriteAttributeIfExist(xw, "minLength", minLength);
                Xmlhelper.WriteAttributeIfExist(xw, "maxLength", maxLength);
                Xmlhelper.WriteAttributeIfExist(xw, "startValue", startValue);
                Xmlhelper.WriteAttributeIfExist(xw, "endValue", endValue);
                Xmlhelper.WriteAttributeIfExist(xw, "interval", interval);
                Xmlhelper.WriteAttributeIfExist(xw, "pattern", pattern);


                xw.WriteEndElement();
            }
        }

        public static TextTypeType GetTypeFromString(string tp)
        {
            foreach (TextTypeType t in Enum.GetValues(typeof(TextTypeType)))
                if (t.ToString().ToLower() == tp.ToLower().Trim())
                    return t;

            // SimpleCodeDataType (2.1)
            if (tp == "AlphaNumeric" || tp == "Alpha")
                return TextTypeType.String;
            else if (tp == "Numeric")
                return TextTypeType.Integer;

            return TextTypeType.Undefined; // Default
        }

        public override string ToString()
        {
            return textType.ToString();
        }

        public bool IsNumeric
        {
            get
            {
                bool answer = false;

                if (textType == TextTypeType.BigInteger ||
                    textType == TextTypeType.Count ||
                    textType == TextTypeType.Decimal ||
                    textType == TextTypeType.Double ||
                    textType == TextTypeType.Float ||
                    textType == TextTypeType.Integer ||
                    textType == TextTypeType.Long ||
                    textType == TextTypeType.Short)
                    answer = true;

                return answer;
            }
        }

        public bool IsMultiLang
        {
            get
            {
                bool answer = false;

                if (textType == TextTypeType.String && isMultiLingual ||
                    textType == TextTypeType.Undefined )
                    answer = true;

                return answer;
            }
            
        }

        public TextTypeBoxes GetBoxEnable(TextTypeType t)
        {
            foreach (TextTypeBoxes tb in TextTypeBoxEnable)
                if (tb.txType == t)
                    return tb;
            return TextTypeBoxEnable[0];
        }
    }

    #region Helper Class for TextFormat persistence

    public class TextFormatHelperForSdmx21Mimic
    {
        private TextFormatType tfType;
        private AnnotationList anList;

        public TextFormatHelperForSdmx21Mimic(TextFormatType tf, AnnotationList anl)
        {
            tfType = tf;
            anList = anl;
        }

        public void RetrieveFormXmlByAnnotation()
        {
            // Default to multilingual if string type
            tfType.isMultiLingual = tfType.TextType == TextTypeType.String;

            // Has SDMX 2.0 annotation for SDMX 2.1 mimic
            Annotation a = anList.GetAnnotationType("SDMX21_IsMultiLingual");
            if (a != null)
            {
                if (a.Title.ToLower() == "false" && tfType.TextType == TextTypeType.String)
                    tfType.isMultiLingual = false;

                anList.Remove(a);
            }
        }

        public void PersistToXmlByAnnotation(XmlTextWriter xw, string prefix, string tag)
        {
            if (tfType.TextType == TextTypeType.String && tfType.isMultiLingual == false)
            {
                Annotation a = new Annotation("SDMX21_IsMultiLingual");
                a.Title = "False";
                anList.Add(a);
                anList.WriteXml(xw, prefix, null);
                anList.Remove(a);
            }
            else
            {
                anList.WriteXml(xw, prefix, null);
            }
        }
    #endregion
    }
}
